using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Middleware
{
    /// <summary>
    /// 操作日志记录中间件
    /// 记录所有HTTP请求的操作日志，包括性能监控
    /// </summary>
    public class OperationLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<OperationLoggingMiddleware> _logger;

        public OperationLoggingMiddleware(RequestDelegate next, ILogger<OperationLoggingMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 跳过对静态资源和健康检查的日志记录
            if (ShouldSkipLogging(context))
            {
                await _next(context);
                return;
            }

            var stopwatch = Stopwatch.StartNew();
            var startTime = DateTime.UtcNow;
            var originalBodyStream = context.Response.Body;
            
            Exception? exception = null;
            string? responseBody = null;

            try
            {
                // 记录请求开始
                LogRequestStart(context);

                // 捕获响应体（可选，用于调试）
                using var responseMemoryStream = new MemoryStream();
                context.Response.Body = responseMemoryStream;

                await _next(context);

                // 获取响应内容
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                responseBody = await new StreamReader(responseMemoryStream).ReadToEndAsync();
                responseMemoryStream.Seek(0, SeekOrigin.Begin);
                await responseMemoryStream.CopyToAsync(originalBodyStream);
            }
            catch (Exception ex)
            {
                exception = ex;
                throw; // 重新抛出异常以保持原有的异常处理流程
            }
            finally
            {
                stopwatch.Stop();
                context.Response.Body = originalBodyStream;

                // 记录操作日志到数据库和控制台
                await LogOperationAsync(context, startTime, stopwatch.ElapsedMilliseconds, exception, responseBody);
            }
        }

        private bool ShouldSkipLogging(HttpContext context)
        {
            var path = context.Request.Path.Value?.ToLower();
            
            // 跳过静态资源
            if (path != null && (
                path.Contains("/css/") ||
                path.Contains("/js/") ||
                path.Contains("/images/") ||
                path.Contains("/favicon.ico") ||
                path.Contains("/health") ||
                path.Contains("/swagger") ||
                path.EndsWith(".css") ||
                path.EndsWith(".js") ||
                path.EndsWith(".ico") ||
                path.EndsWith(".png") ||
                path.EndsWith(".jpg") ||
                path.EndsWith(".gif")))
            {
                return true;
            }

            return false;
        }

        private void LogRequestStart(HttpContext context)
        {
            _logger.LogInformation(
                "开始处理请求: {Method} {Path} | TraceId: {TraceId} | UserId: {UserId} | IP: {IpAddress}",
                context.Request.Method,
                context.Request.Path,
                context.TraceIdentifier,
                GetUserIdFromContext(context),
                GetClientIpAddress(context));
        }

        private async Task LogOperationAsync(HttpContext context, DateTime startTime, long durationMs, Exception? exception, string? responseBody)
        {
            var success = exception == null && context.Response.StatusCode < 400;
            var operationType = DetermineOperationType(context);
            var operationName = $"{context.Request.Method} {context.Request.Path}";

            // 控制台日志记录
            LogToConsole(context, durationMs, success, exception);

            // 数据库日志记录（现在可以启用）
            try
            {
                var operationLogService = context.RequestServices.GetService<IOperationLogAppService>();
                if (operationLogService != null)
                {
                    var additionalData = new Dictionary<string, object>
                    {
                        ["RequestData"] = await GetRequestDataAsync(context),
                        ["ResponseData"] = GetSafeResponseData(responseBody, context.Response.StatusCode),
                        ["IpAddress"] = GetClientIpAddress(context),
                        ["UserAgent"] = context.Request.Headers.UserAgent.ToString(),
                        ["RequestHeaders"] = context.Request.Headers?.ToDictionary(h => h.Key, h => h.Value.ToString()) ?? new Dictionary<string, string>(),
                        ["ResponseHeaders"] = context.Response.Headers?.ToDictionary(h => h.Key, h => h.Value.ToString()) ?? new Dictionary<string, string>(),
                        ["ContentLength"] = context.Response.ContentLength ?? 0,
                        ["ContentType"] = context.Response.ContentType ?? ""
                    };

                    await operationLogService.LogOperationAsync(
                        operationType: operationType,
                        operationName: operationName,
                        description: GetOperationDescription(context, success),
                        userId: GetUserIdFromContext(context),
                        userName: GetUserNameFromContext(context),
                        tenantId: GetTenantIdFromContext(context),
                        correlationId: GetCorrelationIdFromContext(context),
                        requestPath: context.Request.Path,
                        httpMethod: context.Request.Method,
                        duration: durationMs,
                        success: success,
                        additionalData: additionalData);
                }
            }
            catch (Exception logEx)
            {
                _logger.LogError(logEx, "记录操作日志到数据库时发生错误");
            }
        }

        private void LogToConsole(HttpContext context, long durationMs, bool success, Exception? exception)
        {
            var logLevel = DetermineLogLevel(context, success, durationMs);
            var message = $"请求完成: {context.Request.Method} {context.Request.Path} | " +
                         $"状态码: {context.Response.StatusCode} | " +
                         $"耗时: {durationMs}ms | " +
                         $"成功: {success} | " +
                         $"TraceId: {context.TraceIdentifier} | " +
                         $"UserId: {GetUserIdFromContext(context)} | " +
                         $"IP: {GetClientIpAddress(context)}";

            switch (logLevel)
            {
                case LogLevel.Error:
                    _logger.LogError(exception, message);
                    break;
                case LogLevel.Warning:
                    _logger.LogWarning(message);
                    break;
                case LogLevel.Information:
                    _logger.LogInformation(message);
                    break;
                case LogLevel.Debug:
                    _logger.LogDebug(message);
                    break;
            }
        }

        private LogLevel DetermineLogLevel(HttpContext context, bool success, long durationMs)
        {
            if (!success || context.Response.StatusCode >= 500)
                return LogLevel.Error;
            
            if (context.Response.StatusCode >= 400 || durationMs > 5000) // 超过5秒的请求
                return LogLevel.Warning;
                
            if (durationMs > 1000) // 超过1秒的请求
                return LogLevel.Information;
                
            return LogLevel.Debug;
        }

        private string DetermineOperationType(HttpContext context)
        {
            var path = context.Request.Path.Value?.ToLower() ?? "";
            var method = context.Request.Method.ToUpper();

            if (path.Contains("/api/auth/"))
                return "Authentication";
            else if (path.Contains("/api/users/"))
                return "UserManagement";
            else if (path.Contains("/api/audit"))
                return "AuditManagement";
            else if (path.Contains("/api/security"))
                return "SecurityManagement";
            else if (path.Contains("/api/"))
                return "ApiOperation";
            else
                return "WebRequest";
        }

        private string GetOperationDescription(HttpContext context, bool success)
        {
            var baseDescription = $"{context.Request.Method} {context.Request.Path}";
            var statusDescription = success ? "成功" : $"失败 (状态码: {context.Response.StatusCode})";
            return $"{baseDescription} - {statusDescription}";
        }

        private async Task<Dictionary<string, object>> GetRequestDataAsync(HttpContext context)
        {
            var requestData = new Dictionary<string, object>();

            try
            {
                // 查询参数
                if (context.Request.Query.Any())
                {
                    requestData["QueryParameters"] = context.Request.Query.ToDictionary(q => q.Key, q => q.Value.ToString());
                }

                // 路由参数
                if (context.Request.RouteValues.Any())
                {
                    requestData["RouteValues"] = context.Request.RouteValues.ToDictionary(r => r.Key, r => r.Value?.ToString());
                }

                // 请求体（限制大小，避免记录大文件）
                if (context.Request.ContentLength > 0 && context.Request.ContentLength < 10240) // 限制10KB
                {
                    try
                    {
                        context.Request.EnableBuffering();
                        context.Request.Body.Position = 0;
                        using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true);
                        var requestBody = await reader.ReadToEndAsync();
                        context.Request.Body.Position = 0;
                        
                        if (!string.IsNullOrEmpty(requestBody))
                        {
                            requestData["RequestBody"] = requestBody;
                        }
                    }
                    catch
                    {
                        // 读取失败时忽略
                    }
                }
            }
            catch (Exception ex)
            {
                requestData["Error"] = $"获取请求数据时发生错误: {ex.Message}";
            }

            return requestData;
        }

        private object GetSafeResponseData(string? responseBody, int statusCode)
        {
            return new
            {
                StatusCode = statusCode,
                HasBody = !string.IsNullOrEmpty(responseBody),
                BodyLength = responseBody?.Length ?? 0,
                // 只记录小的响应体内容，避免记录大文件
                Body = (responseBody?.Length ?? 0) <= 1024 ? responseBody : "[响应体过大，已省略]"
            };
        }

        #region 辅助方法

        private string? GetTenantIdFromContext(HttpContext context)
        {
            return context.Request.Headers.TryGetValue("X-Tenant-ID", out var tenantValues) ? 
                tenantValues.FirstOrDefault() :
                context.User?.FindFirst("tenant_id")?.Value;
        }

        private string? GetUserIdFromContext(HttpContext context)
        {
            return context.User?.FindFirst("sub")?.Value ?? 
                context.User?.FindFirst("user_id")?.Value ??
                context.User?.Identity?.Name;
        }

        private string? GetUserNameFromContext(HttpContext context)
        {
            return context.User?.FindFirst("name")?.Value ?? 
                context.User?.FindFirst("username")?.Value;
        }

        private string? GetCorrelationIdFromContext(HttpContext context)
        {
            return context.Request.Headers.TryGetValue("X-Correlation-ID", out var correlationValues) ? 
                correlationValues.FirstOrDefault() : 
                context.TraceIdentifier;
        }

        private string GetClientIpAddress(HttpContext context)
        {
            // 尝试从多个位置获取客户端IP地址
            var xForwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (!string.IsNullOrEmpty(xForwardedFor))
            {
                var ips = xForwardedFor.Split(',');
                return ips[0].Trim();
            }

            var xRealIp = context.Request.Headers["X-Real-IP"].FirstOrDefault();
            if (!string.IsNullOrEmpty(xRealIp))
            {
                return xRealIp;
            }

            return context.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
        }

        #endregion
    }
}
